package com.chuangjiangx.member.common.utils;

import com.chuangjiangx.domain.shared.model.PayEntry;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.catalina.connector.InputBuffer;
import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.RequestFacade;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.tomcat.util.buf.ByteChunk;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.http.converter.ByteArrayHttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.net.SocketTimeoutException;
import java.nio.charset.StandardCharsets;

/**
 * HttpServletRequest 请求工具类
 */
@Slf4j
public class RequestUtils {

    public static final RestTemplate REST_TEMPLATE;
    public static final ObjectMapper OBJECT_MAPPER;
    static {
        ByteArrayHttpMessageConverter byteArrayHttpMessageConverter = new ByteArrayHttpMessageConverter();
        byteArrayHttpMessageConverter.setDefaultCharset(StandardCharsets.UTF_8);
        StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter();
        stringHttpMessageConverter.setDefaultCharset(StandardCharsets.UTF_8);
        OBJECT_MAPPER = Jackson2ObjectMapperBuilder
                .json()
                // 反序列化时忽略未知属性造成的异常情况
                .failOnUnknownProperties(false)
                .build();
        MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter(OBJECT_MAPPER);
        mappingJackson2HttpMessageConverter.setDefaultCharset(StandardCharsets.UTF_8);

        CloseableHttpClient httpClient = HttpClientBuilder.create()
                .setMaxConnTotal(200)
                .setMaxConnPerRoute(20)
                .useSystemProperties()
                .build();
        RestTemplateBuilder builder = new RestTemplateBuilder();
        //使用有连接池的apache httpclient
        REST_TEMPLATE = builder.requestFactory(new HttpComponentsClientHttpRequestFactory(httpClient))
                .additionalMessageConverters(byteArrayHttpMessageConverter, stringHttpMessageConverter, mappingJackson2HttpMessageConverter)
                .setConnectTimeout(5000)
                .setReadTimeout(10000)
                .build();

    }

    public static final <T> ReqResponse<T> postForObject(String url,Object request,JavaType responseType,Object... uriVariables){
        T result = null;
        try {
            String json = REST_TEMPLATE.postForObject(url, request, String.class, uriVariables);
            try {
                result = OBJECT_MAPPER.readValue(json,responseType);
            } catch (IOException e) {
                log.warn("{} 无法转换成 {}",json , responseType);
                e.printStackTrace();
            }
            return ReqResponse.success(result);
        } catch (RestClientException e) {
            if(e.getCause() instanceof SocketTimeoutException){
                return ReqResponse.failure(ReqResultStatus.SOCKET_TIMEOUT,e);
            }else{
                return ReqResponse.failure(ReqResultStatus.OTHER,e);
            }
        }
    }

    public static final <T> ReqResponse<T> getForObject(String url,JavaType responseType,Object... uriVariables){
        T result = null;
        try {
            String json = REST_TEMPLATE.getForObject(url, String.class, uriVariables);
            try {
                result = OBJECT_MAPPER.readValue(json,responseType);
            } catch (IOException e) {
                log.warn("{} 无法转换成 {}",json , responseType);
                e.printStackTrace();
            }
            return ReqResponse.success(result);
        } catch (RestClientException e) {
            if(e.getCause() instanceof SocketTimeoutException){
                return ReqResponse.failure(ReqResultStatus.SOCKET_TIMEOUT,e);
            }else{
                return ReqResponse.failure(ReqResultStatus.OTHER,e);
            }
        }
    }

    @Data
    public static class ReqResponse<T>{
        private ReqResultStatus status;
        private T data;
        private Throwable throwable;

        public ReqResponse(ReqResultStatus status, T data) {
            this.status = status;
            this.data = data;
        }

        public ReqResponse(ReqResultStatus status, Throwable throwable) {
            this.status = status;
            this.throwable = throwable;
        }

        public static final <T> ReqResponse<T> success(T data){
            return new ReqResponse<>(ReqResultStatus.SUCCESS,data);
        }
        public static final  ReqResponse failure(ReqResultStatus status,Throwable throwable){
            return new ReqResponse(status,throwable);
        }

        public boolean isSuccess(){
            return ReqResultStatus.SUCCESS.equals(this.status);
        }
        public boolean isSocketTimeout(){
            return ReqResultStatus.SOCKET_TIMEOUT.equals(this.status);
        }
    }

    public enum ReqResultStatus{
        NOT_REQUESTED(-1,"未请求"),
        SUCCESS(0,"请求成功"),
        CONNECT_TIMEOUT(1,"请求连接超时"),
        SOCKET_TIMEOUT(2,"读取请求结果超时"),
        OTHER(3,"其他异常情况");

        public final Integer code;
        public final String msg;

        ReqResultStatus(Integer code, String msg) {
            this.code = code;
            this.msg = msg;
        }
    }

    /**
     * 从请求中获得IP
     * @param request 请求
     * @return 返回IP
     */
    public static String getRequestIp(HttpServletRequest request) {
        String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_CLIENT_IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_X_FORWARDED_FOR");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        if (ip == null || ip.length() == 0) {
            ip = "unknown";
        }
        String[] ips = ip.split(",");
        return ips[0];
    }

    /**
     * 强制读取HttpServletRequest里的报文请求参数
     *
     * @param request 请求对象
     * @return 请求字符串
     */
    public static String forceReadRequestBody(HttpServletRequest request) {
        String bodyParamers = "";
        try {
            InputBuffer inputBuffer = null;
            Request r = null;
            if (request instanceof RequestFacade) {
                r = (Request) FieldUtils.readField(request, "request", true);
            } else if (request instanceof Request) {
                r = (Request) request;
            }
            if (r != null) {
                inputBuffer = (InputBuffer) FieldUtils.readField(r, "inputBuffer", true);
            }
            if (inputBuffer != null) {
                ByteChunk byteChunk = (ByteChunk) FieldUtils.readField(inputBuffer, "bb", true);
                //获取请求报文
                String body = new String(byteChunk.getBuffer(), StandardCharsets.UTF_8);
                int s = body.lastIndexOf("\r\n\r\n");
                if (s == -1) {
                    return bodyParamers;
                }
                int e = body.indexOf('\u0000');
                bodyParamers = body.substring(s + 4, e);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return bodyParamers;
    }

    /**
     * 请求时间 判断时间差 判断效率
     *
     * @param execTime 时间毫秒数
     * @return 小于2000: fast; 大于等于2000小于5000 slow; 大于5000 very slow
     */
    public static String efficiency(long execTime) {
        String efficiency;
        if (execTime < 2000) {
            efficiency = "fast";
        } else if (execTime >= 2000 && execTime < 5000) {
            efficiency = "slow";
        } else {
            efficiency = "very slow";
        }
        return efficiency;
    }

    public static PayEntry getPayEntry(HttpServletRequest request){
        String ua = request.getHeader("User-Agent");
        if (ua.contains("MicroMessenger")) {
            //微信
            return PayEntry.WXPAY;
        } else if (ua.contains("AliApp")) {
            //支付宝
            return PayEntry.ALIPAY;
        } else if (ua.contains("Bestpay")) {
            // 翼支付
            return PayEntry.BESTPAY;
        }
        return null;
    }

}